<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:c="livcos.org/ns/cosmos"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:f="livcos.org/ns/xslt/function"
  exclude-result-prefixes="c f xs">
	
	<xsl:import href="extensions.xsl"/>

  <xsl:param name="livcos_version"/>
  
	<!-- ID of the caller object.-->
	<xsl:param name="callerObjId"/>
	<!-- Path to the Cosmos root relative to the caller object. -->
	<xsl:param name="toRootPath"/>
	
	<xsl:param name="jobId"/>
	<xsl:param name="jobDefId"/>
	<xsl:param name="taskDefId"/>
	<xsl:param name="actor"/>
	<xsl:param name="host"/>

	<!-- for WebJobs. -->
	<xsl:param name="requestPath"/>
	<xsl:param name="sessionId"/>
	<xsl:param name="sessionObjId"/>
	
	<!-- the job object -->
	<xsl:variable name="jobObjId" select="concat('/localhost/system/work/', $jobId)"/>
	<xsl:variable name="jobObject" select="doc(concat($jobObjId, '.cosmos'))/*"/>
	<!-- ID of the caller base object (containing the sys/resolver object).-->
	<xsl:variable name="callerBaseId">
		<xsl:variable name="_r" select="f:find-object('/', $callerObjId, 'sys/resolver')"/>
		<xsl:value-of select="substring($_r, 0, string-length($_r) - 12)"/>
	</xsl:variable>
	<!-- the name of the caller object relative to the caller base object (containing the sys/resolver object).-->
	<xsl:variable name="callerObjName"
	select="substring-after($callerObjId, concat($callerBaseId, '/'))"/>
	<!-- view object ID (containing the sys/resolver object). This equals normally the callerBaseId,
		except the caller URI might have been simplified with a default view. -->
	<xsl:param name="viewObjId" select="$callerBaseId"/>
	
	<xsl:variable name="local-root-folder" select="doc('/localhost/system/root.cosmos')/*/@root"/>
	
	<xsl:function name="f:encode-for-tagname">
		<xsl:param name="val"/>
		<xsl:variable name="tmp"
			select="translate(replace(encode-for-uri(translate($val, ' ', '_')), '~', '_E7'), '%', '_')"/>
		<xsl:value-of select="if (matches($tmp, '^[a-zA-Z_].*')) then $tmp else concat('_', $tmp)"/>
	</xsl:function>

	<!-- Cosmos functions -->
  
	<xsl:function name="f:extract-object-id" as="xs:string">
		<xsl:param name="uri"/>
		<xsl:variable name="p" select="f:extract-path($uri)"/>
		<xsl:variable name="f" select="f:substring-after-last($p, '/')"/>
		<xsl:value-of select="if (contains($f, '.')) then f:substring-before-last($p, '.') else $p"/>
	</xsl:function>
	
	<xsl:function name="f:object-name" as="xs:string">
		<xsl:param name="obj"/>
		<xsl:value-of select="tokenize(f:object-relative-name($obj), '/')[last()]"/>
	</xsl:function>
	
	<xsl:function name="f:object-relative-name" as="xs:string">
		<xsl:param name="node" as="node()?"/>
		<xsl:variable name="absId" select="($node/ancestor-or-self::*/@id[starts-with(., '/')])[last()]"/>
		<xsl:choose>
			<xsl:when test="contains($absId, '#')">
				<xsl:value-of select="substring-before($absId, '#')"/>
			</xsl:when>
			<xsl:when test="$absId">
				<xsl:value-of select="$absId"/>
			</xsl:when>
			<xsl:when test="$node/ancestor-or-self::c:*">
				<xsl:value-of select="string-join(for $c in $node/ancestor-or-self::c:* return local-name($c), '/')"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="f:strip-after(tokenize(base-uri($node), '/')[last()], '#')"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<xsl:function name="f:object-id" as="xs:string">
		<xsl:param name="node" as="node()?"/>
		<xsl:variable name="relativeName" select="f:object-relative-name($node)"/>
		<xsl:choose>
			<xsl:when test="starts-with($relativeName, '/')">
				<xsl:value-of select="$relativeName"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of	select="resolve-uri($relativeName,f:extract-object-id(base-uri($node)))"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<xsl:function name="f:entity-id" as="xs:string">
		<xsl:param name="node" as="node()"/>
		<xsl:variable name="_id" select="$node/ancestor-or-self::*[@id][1]/@id"/>
		<xsl:variable name="_val">
			<xsl:if test="not(starts-with($_id, '/'))">
				<xsl:value-of select="f:object-id($node)"/>
				<xsl:text>#</xsl:text>
			</xsl:if>
			<xsl:value-of select="$_id"/>
		</xsl:variable>
		<xsl:value-of select="$_val"/>
	</xsl:function>
	
	<xsl:function name="f:abs-ref" as="xs:string">
		<xsl:param name="ref" as="node()"/>
		<xsl:variable name="objId" select="f:object-id($ref)"/>
		<xsl:value-of select="if (starts-with($ref, '#')) then concat($objId, $ref) else resolve-uri($ref, $objId)"/>
	</xsl:function>
	
	<xsl:function name="f:cosmos-uri" as="element()" exclude-result-prefixes="#all">
    <xsl:param name="str"/>
		<xsl:param name="base" as="node()"/>
    <uri>
    	<xsl:variable name="_uri" select="f:uri($str)"/>
    	<xsl:variable name="_base" select="f:object-id($base)"/>
			<xsl:variable name="_objId"
				select="if ($_uri/path) then resolve-uri($_uri/path, $_base) else $_base"/>
    	<obj name="{tokenize($_objId, '/')[last()]}"><xsl:value-of select="$_objId"/></obj>
    	<xsl:if test="$_uri/fragment">
      	<id><xsl:value-of select="$_uri/fragment"/></id>
      </xsl:if>
    </uri>
  </xsl:function>
  
	<xsl:function name="f:resolve-object-id" as="xs:string">
		<xsl:param name="objId"/>
		<xsl:param name="base" as="node()"/>
		<xsl:value-of select="resolve-uri($objId, f:object-id($base))"/>
	</xsl:function>
	
	<!--
		builds an element with the namespace context, encoded in the given string.
				prefix=namespaceURI prefix1=namespaceURI1 ...
	-->
	<xsl:function name="f:parse-ns" as="element()">
		<xsl:param name="code"/>
		<xsl:element name="e">
			<xsl:for-each select="tokenize($code, ' ')">
				<xsl:namespace name="{substring-before(., '=')}">
					<xsl:value-of select="substring-after(., '=')"/>
				</xsl:namespace>
			</xsl:for-each>
		</xsl:element>
	</xsl:function>
	
	<!-- Returns the entity, addressed by the given URI.
		Relative URIs are resolved based on the object ID of the given base node. -->
	<xsl:function name="f:entity">
		<xsl:param name="uri" as="xs:string"/>
		<xsl:param name="base" as="node()"/>
		<xsl:variable name="_uri" select="f:cosmos-uri($uri, $base)"/>
		<xsl:choose>
			<xsl:when test="not(f:object-available($_uri/obj))">
			</xsl:when>
			<xsl:when test="$_uri/id">
				<xsl:sequence select="doc($_uri/obj)//*[@id = $_uri/id]"/>
			</xsl:when>
			<xsl:when test="f:object-available($_uri/obj, 'content')">
				<xsl:sequence select="doc($_uri/obj)/*"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:sequence select="doc(concat($_uri/obj, '.meta2'))/*"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<xsl:function name="f:entity">
		<xsl:param name="ref" as="node()"/>
		<xsl:variable name="_uri" select="f:cosmos-uri($ref, $ref)"/>
		<xsl:choose>
			<xsl:when test="$_uri/obj = f:object-id($ref)">
				<xsl:sequence select="root($ref)//*[@id = $_uri/id]"/>
			</xsl:when>
			<xsl:when test="not(f:object-available($_uri/obj))">
			</xsl:when>
			<xsl:when test="$_uri/id">
				<xsl:sequence select="doc($_uri/obj)//*[@id = $_uri/id]"/>
			</xsl:when>
			<xsl:when test="f:object-available($_uri/obj, 'content')">
				<xsl:sequence select="doc($_uri/obj)/*"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:sequence select="doc(concat($_uri/obj, '.meta2'))/*"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<!-- Selects the first entity within a collection, addressed by the given URI. -->
	<xsl:function name="f:select-entity">
		<xsl:param name="uri" as="xs:string"/>
		<xsl:param name="collection" as="node()*"/>
		<xsl:variable name="_direct" select="$collection/descendant-or-self::*[@id = $uri][1]"/>
		<xsl:choose>
			<xsl:when test="$_direct">
				<xsl:sequence select="$_direct"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="id" select="substring-after($uri, '#')"/>
				<xsl:sequence select="$collection/descendant-or-self::*[@id = $id][1]"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<!-- escaped output -->
	
	<xsl:function name="f:escape">
		<xsl:param name="text"/>
		<xsl:value-of
			select="replace(replace(replace($text,'&amp;','&amp;amp;'),'&lt;','&amp;lt;'),'&gt;','&amp;gt;')" />
	</xsl:function>
	
	<xsl:template match="text()" mode="escape root_escape">
		<xsl:value-of select="f:escape(.)" />
	</xsl:template>
	
	<xsl:template match="@*" mode="escape">
		<xsl:text> </xsl:text>
		<xsl:value-of select="name(.)" />
		<xsl:text>="</xsl:text>
		<xsl:value-of select="f:escape(.)" />
		<xsl:text>"</xsl:text>
	</xsl:template>
	
	<xsl:template match="*" mode="escape">
		<xsl:param name="root"/>
		<xsl:text disable-output-escaping="yes"><![CDATA[<]]></xsl:text>
		<xsl:value-of select="name(.)" />
		<xsl:apply-templates select="@*" mode="escape"/>
		<xsl:for-each select="namespace::*[local-name(.) != 'xml']">
			<xsl:variable name="prefix" select="local-name(.)"/>
			<xsl:variable name="nsUri" select="."/>
			<xsl:if test="not(../../namespace::*[(local-name(.) = $prefix) and (. = $nsUri)]) or $root">
				<xsl:text> xmlns</xsl:text>
				<xsl:if test="$prefix != ''">:</xsl:if>
				<xsl:value-of select="$prefix" />="<xsl:value-of select="$nsUri" />
				<xsl:text>"</xsl:text>
			</xsl:if>
		</xsl:for-each>
		<xsl:choose>
			<xsl:when test="node()">
				<xsl:text><![CDATA[>]]></xsl:text>
				<xsl:apply-templates select="node()" mode="escape"/>
				<xsl:text><![CDATA[<]]>/</xsl:text>
				<xsl:value-of select="name(.)" />
				<xsl:text><![CDATA[>]]></xsl:text>
			</xsl:when>
			<xsl:otherwise>
				<xsl:text>/<![CDATA[>]]></xsl:text>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	
	<xsl:template match="*" mode="root_escape">
		<xsl:apply-templates select="." mode="escape">
			<xsl:with-param name="root" select="1"/>
		</xsl:apply-templates>
	</xsl:template>
	
	
	<!-- link URL converters -->
	<xsl:function name="f:ref-to-uri" as="xs:string">
		<xsl:param name="view" as="node()?"/>
		<xsl:param name="ref" as="node()"/>
		<xsl:param name="default_extension" as="xs:string"/>
		<xsl:variable name="_uri" select="f:uri($ref)"/>
		<xsl:choose>
			<xsl:when test="starts-with($ref, '#')">
				<xsl:value-of select="$ref"/>
			</xsl:when>
			<xsl:when test="starts-with($ref, 'javascript:')">
				<xsl:value-of select="$ref"/>
			</xsl:when>
			<xsl:when test="not(f:isValidUrl($ref))">
				<xsl:value-of select="$ref"/>
			</xsl:when>
			<xsl:when test="$_uri/scheme">
				<xsl:value-of select="$ref"/>
			</xsl:when>
			<xsl:when test="normalize-space($ref) != ''">
				<xsl:variable name="_tmp">
					<xsl:variable name="_view" 
						select="if (normalize-space($view) != '') then resolve-uri($view, f:object-id($view)) else $viewObjId"/>
					<xsl:variable name="_u1" select="replace($_uri/path, '/+$', '')"/>
					<xsl:variable name="_ref" select="f:cut-start(resolve-uri($_u1, f:object-id($ref)), 'cosmos:')"/>
					<xsl:variable name="_ext"	select="if (matches($_u1,'\.[^/]+$')) then '' else $default_extension"/>
					<xsl:value-of select="f:caller-uri(concat($_view, $_ref, $_ext))"/>
					<xsl:if test="$_uri/query">?<xsl:value-of select="$_uri/query"/></xsl:if>
					<xsl:if test="$_uri/fragment">#<xsl:value-of select="$_uri/fragment"/></xsl:if>
				</xsl:variable>
				<xsl:value-of select="$_tmp"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:text>#</xsl:text>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<!-- Sequence of available object IDs, built with the namespace URIs and their parents, contained in a given node. -->
	<xsl:function name="f:find-objs-for-namespaces">
		<xsl:param name="node" as="node()"/>
		<xsl:param name="baseUris" as="xs:string*"/>
		<xsl:param name="objName" as="xs:string"/>
		<xsl:variable name="namespaceUris" as="xs:string*">
			<xsl:for-each select="distinct-values($node//namespace::*)">
				<xsl:value-of select="f:object-uri-from-namespace(.)"/>
			</xsl:for-each>
		</xsl:variable>
		<xsl:sequence select="f:find-objects($baseUris, $namespaceUris, $objName)"/>
	</xsl:function>
	
	<xsl:function name="f:object-uri-from-namespace" as="xs:string">
		<xsl:param name="nsUri" as="xs:string"/>
		<xsl:variable name="_val">
			<xsl:for-each select="tokenize(replace($nsUri, '://', '/'), '/')">
				<xsl:if test="position() > 1">/</xsl:if>
				<xsl:if test="matches(., '^[0-9]')">_</xsl:if>
				<xsl:value-of select="."/>
			</xsl:for-each>
		</xsl:variable>
		<xsl:value-of select="$_val"/>
	</xsl:function>

	<xsl:function name="f:find-data-object">
		<xsl:param name="ref" as="xs:string*"/>
		<xsl:value-of select="f:find-data-object($ref, $viewObjId)"/>
	</xsl:function>
	
	<xsl:function name="f:find-data-object">
		<xsl:param name="ref" as="xs:string*"/>
		<xsl:param name="viewObjId" as="xs:string"/>
		<xsl:variable name="_ref" select="f:cut-start($ref, '/')"/>
		<xsl:choose>
			<xsl:when test="normalize-space($_ref) = ''">
			</xsl:when>
			<xsl:when test="f:object-available(concat('/', $_ref))">
				<xsl:value-of select="concat('/', $_ref)"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:variable name="rel" select="if (contains($_ref, '/data/')) then $_ref else f:prefix('data/', $_ref)"/>
				<xsl:variable name="obj" select="f:find-object('/', $viewObjId, $rel)"/>
				<xsl:value-of select="if ($obj) then $obj else f:find-object('/', $viewObjId, $_ref)"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
	<xsl:function name="f:resolve-file-path">
		<xsl:param name="path" as="xs:string"/>
		<xsl:choose>
			<xsl:when test="normalize-space($path) = ''">
			</xsl:when>
			<xsl:when test="starts-with($path, 'file:')">
				<xsl:value-of select="$path"/>
			</xsl:when>
			<xsl:when test="starts-with($path, '/')">
				<xsl:value-of select="concat('file:', $path)"/>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="concat('file:', $local-root-folder, $path)"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:function>
	
</xsl:stylesheet>
